package titan.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.URIUtil;
import titan.messages.Messages;

/**
 * Class with static methods to copy files and folders
 * as well as read and write text files and
 * verify the existence of a list of files and folders in a given directory
 */
public abstract class FileUtils 
{
	/**
	 * Copy file content from source file to destination file
	 * @param source - the source file
	 * @param destination - the destination file
	 * @throws Exception if there was an error while reading or writing files
	 */
	public static void copyFile(File source, File destination) throws Exception {
		// Create file (if it doesn't exist)
		if(!destination.exists())
			try {
				destination.createNewFile();
			} catch (Exception e) {
				throw new Exception(Messages.UnableCreate+destination.getAbsolutePath());
			}
		// Create input and output Streams
		FileInputStream fisSource;
		try {
			fisSource = new FileInputStream(source);
		} catch (Exception e) {
			throw new Exception(Messages.SourceNFound+source.getAbsolutePath());
		}
		FileOutputStream fisDestin;
		fisDestin = new FileOutputStream(destination);

		// Create Channels
		FileChannel fcSource = fisSource.getChannel();
		FileChannel fcDestin = fisDestin.getChannel();

		// Copy from a Channel to the other
		try {
			fcSource.transferTo(0, fcSource.size(), fcDestin);
		} catch (Exception e) {
			throw new Exception(Messages.UnableCopy+source.getAbsolutePath()+Messages.To+destination.getAbsolutePath());
		}

		// Close Streams
		fisSource.close();
		fisDestin.close();
	}

	/**
	 * Copy all files from source folder to destination folder using {@link FileUtils#copyFile(File, File) copyFile}
	 * @param source - the source folder
	 * @param destination - the destination folder
	 * @throws Exception if source or destination is not a folder or if there was
	 * an error while copying files from source to destination
	 */
	public static void copyAllFiles(File source, File destination) throws Exception {
		if (!source.isHidden()) {
			// Create destination directory, if it doesn't exist.
			if (!destination.exists())
				destination.mkdir();

			// Throw exception if source or destination is not a directory
			if (!source.isDirectory())
				throw new Exception(Messages.SourceInvalid+source.getAbsolutePath());
			if (!destination.isDirectory())
				throw new Exception(Messages.DestinationInvalid+destination.getAbsolutePath());

			// Create array with all the source directory files
			File[] files = source.listFiles();

			// Recursive call and files copy
			for (File file : files)
				if (file.isDirectory())
					copyAllFiles(file, new File(destination + File.separator + file.getName()));
				else
					copyFile(file, new File(destination + File.separator + file.getName()));
		}
	}

	/**
	 * Read a text file and store its content in a string
	 * @param textFile - the text file
	 * @return the file content
	 * @throws Exception if file doesn't exist or if there was an error while reading
	 * file content
	 */
	public static String readTextFile(File textFile) throws Exception {
		// Create input stream
		InputStream is;
		try {
			is = new FileInputStream(textFile);
		} catch (Exception e) {
			throw new Exception(Messages.SourceNFound+textFile.getAbsolutePath());
		}
		// Create a buffer with file size
		byte[] buffer = new byte[(int)textFile.length()];
		// Read content from input stream to the file buffer
		try {
			is.read(buffer);
		} catch (Exception e) {
			throw new Exception(Messages.UnableRead+textFile.getAbsolutePath());
		}
		// Return string with buffer content
		return new String(buffer);
	}

	/**
	 * Write a content string into a file
	 * @param textFile - the file to which content will be written
	 * @param content - the content to be written
	 * @throws Exception - if file can't be opened or if there was an error while writing file
	 */
	public static void writeTextFile(File textFile, String content) throws Exception{
		// Create a file writer
		FileWriter out;
		try {
			out = new FileWriter(textFile);
		} catch (Exception e) {
			throw new Exception(textFile.getAbsolutePath()+Messages.CantBeOpened);
		}
		// Write content to the file
		try {
			out.write(content);
		} catch (Exception e) {
			throw new Exception(Messages.UnableWrite+textFile.getAbsolutePath());
		}
		// Close file writer
		out.close();
	}

	/**
	 * Get the reference to the resource file
	 * @param path - the path to the resource
	 * @return a pointer to the resource file
	 * @throws Exception if it can't get the resource file
	 */
	public static File getResourceFile(String path) throws Exception {
		try {
			return URIUtil.toFile( FileLocator.toFileURL(FileUtils.class.getResource(path)).toURI() );
		} catch (Exception e) {
			throw new Exception(Messages.UnableGetResrc+path+Messages.PluginError);
		}
	}

	/**
	 * Checks if the given folder is a valid empty folder. It occurs when:
	 * parent folder exists but the folder doesn't; or folder exists and it's empty
	 * @param path - the path to the folder
	 * @return true if the folder is a valid empty folder or false otherwise
	 * @throws Exception if parent folder doesn't exist
	 */
	public static boolean isEmptyFolder(String path) throws Exception {
		File folder = new File(path);
		File parent = folder.getParentFile();
		// throw exception if parent folder doesn't exist
		if(!parent.exists())
			throw new Exception(Messages.ParentFoldNFound+parent.getAbsolutePath());
		// if parent exists but folder doesn't, it's a valid empty folder
		if(!folder.exists())
			return true;
		// folder exists and it's empty
		File[] children = folder.listFiles();
		if(children.length == 0)
			return true;
		// folder exists and it's not empty
		return false;
	}

	/**
	 * Tests if the folder contains a file list
	 * @param path - the path to the folder
	 * @param listingFile - the file containing a file list
	 * @return true if it contains the valid list or false otherwise
	 * @throws Exception
	 */
	public static boolean testFileList(String path, File listingFile) throws Exception {
		if(!path.endsWith(File.separator))
			path += File.separator;
		File folder = new File(path);
		if(!folder.exists() || !folder.isDirectory())
			return false;
		String fileList = readTextFile(listingFile);
		fileList = fileList.replace('\\', File.separatorChar);
		String[] listedFileArray = fileList.replace("\r", "").split("\n");
		for(int i = 0; i < listedFileArray.length; i++)
			if(!( new File(path+listedFileArray[i]).exists() ) )
				return false;
		return true;
	}
}
